package com.example.rfidwms;

import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.zebra.rfid.api3.ACCESS_OPERATION_CODE;
import com.zebra.rfid.api3.ACCESS_OPERATION_STATUS;
import com.zebra.rfid.api3.Antennas;
import com.zebra.rfid.api3.ENUM_TRANSPORT;
import com.zebra.rfid.api3.ENUM_TRIGGER_MODE;
import com.zebra.rfid.api3.HANDHELD_TRIGGER_EVENT_TYPE;
import com.zebra.rfid.api3.INVENTORY_STATE;
import com.zebra.rfid.api3.InvalidUsageException;
import com.zebra.rfid.api3.MEMORY_BANK;
import com.zebra.rfid.api3.OperationFailureException;
import com.zebra.rfid.api3.RFIDReader;
import com.zebra.rfid.api3.ReaderDevice;
import com.zebra.rfid.api3.Readers;
import com.zebra.rfid.api3.RfidEventsListener;
import com.zebra.rfid.api3.RfidReadEvents;
import com.zebra.rfid.api3.RfidStatusEvents;
import com.zebra.rfid.api3.SESSION;
import com.zebra.rfid.api3.SL_FLAG;
import com.zebra.rfid.api3.START_TRIGGER_TYPE;
import com.zebra.rfid.api3.STATUS_EVENT_TYPE;
import com.zebra.rfid.api3.STOP_TRIGGER_TYPE;
import com.zebra.rfid.api3.TagAccess;
import com.zebra.rfid.api3.TagData;
import com.zebra.rfid.api3.TriggerInfo;

import java.util.ArrayList;

class RFIDHandler implements Readers.RFIDReaderEventHandler {

    final static String TAG = "RFID_HANDLER";
    // RFID Reader
    private static Readers readers;
    private static ArrayList<ReaderDevice> availableRFIDReaderList;
    private static ReaderDevice readerDevice;
    private static RFIDReader reader;
    private EventHandler eventHandler;
    // UI and context
    TextView textView;
    private AppCompatActivity context;
    // general
    private int MAX_POWER = 270;
    // In case of RFD8500 change reader name with intended device below from list of paired RFD8500
    String readername = "RFD8500123";

    void onCreate(AppCompatActivity activity) {
        // application context
        context = activity;
//        背景类型判断
        if(activity instanceof WriteTagActivity){
            // Status UI
            textView = ((WriteTagActivity) activity).statusTextViewRFID;
        }
        if(activity instanceof InTagActivity){
            textView = ((InTagActivity) activity).statusTextViewRFID;
        }
        if(activity instanceof OutTagActivity){
            textView = ((OutTagActivity) activity).statusTextViewRFID;
        }
        if(activity instanceof CheckTagActivity){
            textView = ((CheckTagActivity) activity).statusTextViewRFID;
        }
        // SDK
        InitSDK();
    }

    public void writeTag(final String toTag, final String writeData) {
        Log.d("TAG1", "WriteTag " + writeData);

        TagData tagData = null;
        TagAccess tagAccess = new TagAccess();
        final TagAccess.WriteAccessParams writeAccessParams = tagAccess.new WriteAccessParams();
        // 内部类， 专门封装了数据， 用来做写入的参数
//        writeAccessParams.setAccessPassword(0);
//        writeAccessParams.setMemoryBank(MEMORY_BANK.MEMORY_BANK_EPC);// EPC 和 USE 好像都行
        writeAccessParams.setOffset(2);// 这里设置为2 ，我也不知道 为啥， 反正0 不对。
        writeAccessParams.setWriteData(writeData);// 写入的数据
//        writeAccessParams.setWriteRetries(2);// 失败重试次数
        writeAccessParams.setWriteDataLength(writeData.length() / 4);
        // 新task 防止阻塞主线程
        new AsyncTask<Void, Void, Boolean>() {
            private InvalidUsageException invalidUsageException;
            private OperationFailureException operationFailureException;
            private Boolean bResult = false;

            @Override
            protected Boolean doInBackground(Void... voids) {
                try {
                    Boolean bFilter = (toTag.length() <= 24 );

                    Log.e(TAG, "doInBackground: before write");
                    // 主要传的参数是前两个， 第一个是tag id， 第二个是参数的封装类， 第四个参数是返回值（sdk）上这么说的，试过但没成功
                    reader.Actions.TagAccess.writeWait(toTag, writeAccessParams, null, null, bFilter, false);
                    Log.e(TAG, "doInBackground: after write");

                    bResult = true;
                } catch (InvalidUsageException e) {
                    invalidUsageException = e;
                    e.printStackTrace();
                } catch (OperationFailureException e) {
                    operationFailureException = e;
                    Log.e(TAG, "doInBackground: " + e.getVendorMessage() + "\n" + e.getResults() + "\n case" + e.getCause());
                    e.printStackTrace();
                }
                return bResult;
            }
        }.execute();

    }

    private boolean isReaderConnected() {
        if (reader != null && reader.isConnected())
            return true;
        else {
            Log.d(TAG, "reader is not connected");
            return false;
        }
    }

    //
    //  Activity life cycle behavior
    //

    String onResume() {
        return connect();
    }

    void onPause() {
        disconnect();
    }

    void onDestroy() {
        dispose();
    }

    //
    // RFID SDK
    //

    private void InitSDK() {
        Log.d(TAG, "InitSDK");
        if (readers == null) {
            new CreateInstanceTask().execute();
        } else
            new ConnectionTask().execute();
    }

    // Enumerates SDK based on host device
    private class CreateInstanceTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... voids) {
            Log.d(TAG, "CreateInstanceTask");
            // Based on support available on host device choose the reader type
            InvalidUsageException invalidUsageException = null;
            readers = new Readers(context, ENUM_TRANSPORT.ALL);
            availableRFIDReaderList = readers.GetAvailableRFIDReaderList();
            if (invalidUsageException != null) {
                readers.Dispose();
                readers = null;
                if (readers == null) {
                    readers = new Readers(context, ENUM_TRANSPORT.BLUETOOTH);
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            new ConnectionTask().execute();
        }
    }

    private class ConnectionTask extends AsyncTask<Void, Void, String> {
        @Override
        protected String doInBackground(Void... voids) {
            Log.d(TAG, "ConnectionTask");
            GetAvailableReader();
            if (reader != null)
                return connect();
            return "Failed to find or connect reader";
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            textView.setText(result);
        }
    }

    private synchronized void GetAvailableReader() {
        Log.d(TAG, "GetAvailableReader");
        if (readers != null) {
            readers.attach(this);
            if (readers.GetAvailableRFIDReaderList() != null) {
                availableRFIDReaderList = readers.GetAvailableRFIDReaderList();
                if (availableRFIDReaderList.size() != 0) {
                    // if single reader is available then connect it
                    if (availableRFIDReaderList.size() == 1) {
                        readerDevice = availableRFIDReaderList.get(0);
                        reader = readerDevice.getRFIDReader();
                    } else {
                        // search reader specified by name
                        for (ReaderDevice device : availableRFIDReaderList) {
                            if (device.getName().equals(readername)) {
                                readerDevice = device;
                                reader = readerDevice.getRFIDReader();
                            }
                        }
                    }
                }
            }
        }
    }

    // handler for receiving reader appearance events
    @Override
    public void RFIDReaderAppeared(ReaderDevice readerDevice) {
        Log.d(TAG, "RFIDReaderAppeared " + readerDevice.getName());
        new ConnectionTask().execute();
    }

    @Override
    public void RFIDReaderDisappeared(ReaderDevice readerDevice) {
        Log.d(TAG, "RFIDReaderDisappeared " + readerDevice.getName());
        if (readerDevice.getName().equals(reader.getHostName()))
            disconnect();
    }


    private synchronized String connect() {
        if (reader != null) {
            Log.d(TAG, "connect " + reader.getHostName());
            try {
                if (!reader.isConnected()) {
                    // Establish connection to the RFID Reader
                    reader.connect();
                    ConfigureReader();
                    return reader.getHostName()+" connected";
                }
            } catch (InvalidUsageException e) {
                e.printStackTrace();
            } catch (OperationFailureException e) {
                e.printStackTrace();
                Log.d(TAG, "OperationFailureException " + e.getVendorMessage());
                String des = e.getResults().toString();
                return "Connection failed" + e.getVendorMessage() + " " + des;
            }
        }
        return "";
    }

    private void ConfigureReader() {
        Log.d(TAG, "ConfigureReader " + reader.getHostName());
        if (reader.isConnected()) {
            TriggerInfo triggerInfo = new TriggerInfo();
            triggerInfo.StartTrigger.setTriggerType(START_TRIGGER_TYPE.START_TRIGGER_TYPE_IMMEDIATE);
            triggerInfo.StopTrigger.setTriggerType(STOP_TRIGGER_TYPE.STOP_TRIGGER_TYPE_IMMEDIATE);

            try {
                // receive events from reader
                if (eventHandler == null)
                    eventHandler = new EventHandler();
                reader.Events.addEventsListener(eventHandler);
                // HH event
                reader.Events.setHandheldEvent(true);
                // tag event with tag data
                reader.Events.setTagReadEvent(true);
                reader.Events.setAttachTagDataWithReadEvent(false);
                // set trigger mode as rfid so scanner beam will not come
                reader.Config.setTriggerMode(ENUM_TRIGGER_MODE.RFID_MODE, true);
                // set start and stop triggers
                reader.Config.setStartTrigger(triggerInfo.StartTrigger);
                reader.Config.setStopTrigger(triggerInfo.StopTrigger);
                // power levels are index based so maximum power supported get the last one
                MAX_POWER = reader.ReaderCapabilities.getTransmitPowerLevelValues().length - 1;
                // set antenna configurations
                Antennas.AntennaRfConfig config = reader.Config.Antennas.getAntennaRfConfig(1);
                if(context instanceof WriteTagActivity){
                    config.setTransmitPowerIndex(100);

                }else{
                    config.setTransmitPowerIndex(MAX_POWER);
                }
                config.setrfModeTableIndex(0);
                config.setTari(0);
                reader.Config.Antennas.setAntennaRfConfig(1, config);
                // Set the singulation control
                Antennas.SingulationControl s1_singulationControl = reader.Config.Antennas.getSingulationControl(1);
                s1_singulationControl.setSession(SESSION.SESSION_S0);
                s1_singulationControl.Action.setInventoryState(INVENTORY_STATE.INVENTORY_STATE_A);
                s1_singulationControl.Action.setSLFlag(SL_FLAG.SL_ALL);
                reader.Config.Antennas.setSingulationControl(1, s1_singulationControl);
                // delete any prefilters
                reader.Actions.PreFilters.deleteAll();
                //

                System.out.println("\nTag Locating Reporting: " + reader.ReaderCapabilities.isTagLocationingSupported());
            } catch (InvalidUsageException | OperationFailureException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void disconnect() {
        Log.d(TAG, "disconnect " + reader);
        try {
            if (reader != null) {
                reader.Events.removeEventsListener(eventHandler);
                reader.disconnect();
                context.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("Disconnected");
                    }
                });
            }
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        } catch (OperationFailureException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private synchronized void dispose() {
        try {
            if (readers != null) {
                reader = null;
                readers.Dispose();
                readers = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    synchronized void performInventory() {
        // check reader connection
        if (!isReaderConnected())
            return;
        TagAccess tagAccess = new TagAccess();
        TagAccess.ReadAccessParams readAccessParams = tagAccess.new ReadAccessParams();
        readAccessParams.setMemoryBank(MEMORY_BANK.MEMORY_BANK_TID);
        try {
//            reader.Actions.Inventory.perform();
            reader.Actions.TagAccess.readEvent(readAccessParams, null, null);
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        } catch (OperationFailureException e) {
            e.printStackTrace();
        }
    }

    synchronized void stopInventory() {
        // check reader connection
        if (!isReaderConnected())
            return;
        try {
//            reader.Actions.Inventory.stop();
            reader.Actions.TagAccess.stopAccess();
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        } catch (OperationFailureException e) {
            e.printStackTrace();
        }
    }

    // Read/Status Notify handler
    // Implement the RfidEventsLister class to receive event notifications
    public class EventHandler implements RfidEventsListener {
        // Read Event Notification
        public void eventReadNotify(RfidReadEvents e) {
            // Recommended to use new method getReadTagsEx for better performance in case of large tag population
            TagData[] myTags = reader.Actions.getReadTags(100);

            if (myTags != null) {
                for (int index = 0; index < myTags.length; index++) {
                    Log.d(TAG, "Tag ID " + myTags[index].getTagID());
                    if (myTags[index].getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_READ &&
                            myTags[index].getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) {
                        if (myTags[index].getMemoryBankData().length() > 0) {
                            Log.d(TAG, " Mem Bank Data " + myTags[index].getMemoryBankData());
                        }
                    }
                    if (myTags[index].isContainsLocationInfo()) {
                        short dist = myTags[index].LocationInfo.getRelativeDistance();
                        Log.d(TAG, "Tag relative distance " + dist);
                    }
                }
                // possibly if operation was invoked from async task and still busy
                // handle tag data responses on parallel thread thus THREAD_POOL_EXECUTOR
                new AsyncDataUpdate().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, myTags);
            }
        }

        // Status Event Notification
        public void eventStatusNotify(RfidStatusEvents rfidStatusEvents) {
            Log.d(TAG, "Status Notification: " + rfidStatusEvents.StatusEventData.getStatusEventType());
            if (rfidStatusEvents.StatusEventData.getStatusEventType() == STATUS_EVENT_TYPE.HANDHELD_TRIGGER_EVENT) {
                if (rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldEvent() == HANDHELD_TRIGGER_EVENT_TYPE.HANDHELD_TRIGGER_PRESSED) {
                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... voids) {
                            if(context instanceof WriteTagActivity){
                                ((WriteTagActivity) context).handleTriggerPress(true);
                            }
                            if(context instanceof InTagActivity){
                                ((InTagActivity) context).handleTriggerPress(true);
                            }
                            if(context instanceof OutTagActivity){
                                ((OutTagActivity) context).handleTriggerPress(true);
                            }
                            if(context instanceof CheckTagActivity){
                                ((CheckTagActivity) context).handleTriggerPress(true);
                            }
                            return null;
                        }
                    }.execute();
                }
                if (rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldEvent() == HANDHELD_TRIGGER_EVENT_TYPE.HANDHELD_TRIGGER_RELEASED) {
                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... voids) {
                            if(context instanceof WriteTagActivity){
                                ((WriteTagActivity) context).handleTriggerPress(false);
                            }
                            if(context instanceof InTagActivity){
                                ((InTagActivity) context).handleTriggerPress(false);
                            }
                            if(context instanceof OutTagActivity){
                                ((OutTagActivity) context).handleTriggerPress(false);
                            }
                            if(context instanceof CheckTagActivity){
                                ((CheckTagActivity) context).handleTriggerPress(false);
                            }
                            return null;
                        }
                    }.execute();
                }
            }
        }
    }

    private class AsyncDataUpdate extends AsyncTask<TagData[], Void, Void> {
        @Override
        protected Void doInBackground(TagData[]... params) {
            if(context instanceof WriteTagActivity){
                ((WriteTagActivity) context).handleTagdata(params[0]);
            }
            if(context instanceof InTagActivity){
                ((InTagActivity) context).handleTagdata(params[0]);
            }
            if(context instanceof OutTagActivity){
                ((OutTagActivity) context).handleTagdata(params[0]);
            }
            if(context instanceof CheckTagActivity){
                ((CheckTagActivity) context).handleTagdata(params[0]);
            }
            return null;
        }
    }

}
